package de.hska.algo.labor2010.strecken;

/**
 * class Line holds starting and ending {@link Point} , points are sorted by
 * x-value, smaller x-values make starting point
 * 
 * @author Daniela Brenner
 * @version 1.0
 */

public class Line {
final double MYEPSILON = 1.9e-12;
	/**
	 */
	private Point p1;
	/**
	 */
	private Point p2;

	 private double m;
	 public double getM() {
		return m;
	}

	public double getC() {
		return c;
	}

	private double c;

	/**
	 * constructor sorts points by x-value
	 * 
	 * @param p1
	 *            starting or ending {@link Point}
	 * @param p2
	 *            starting or ending {@link Point}
	 * 
	 */
	public Line(Point p1, Point p2) {
		initialize(p1, p2);
		
	}

	/**
	 * constructor sorts points by x-value
	 * 
	 * @param x1
	 *            x-value for starting or ending {@link Point}
	 * @param y1
	 *            y-value for starting or ending {@link Point}
	 * @param x2
	 *            x-value for starting or ending {@link Point}
	 * @param y2
	 *            y-value for starting or ending {@link Point}
	 * 
	 */
	public Line(double x1, double y1, double x2, double y2) {

		initialize(new Point(x1, y1), new Point(x2, y2));
		
	}

	/**
	 * method is called in constructor, sorts points by value
	 * 
	 * @param p1
	 *            starting or ending {@link Point}
	 * @param p2
	 *            starting or ending {@link Point}
	 */

	private void initialize(Point p1, Point p2) {
		if (p1 == null || p2 == null) {
			throw new IllegalArgumentException(
					"Points must not be null in order to create a line");
		}
		if (p1.getX()  <= p2.getX()) {
			this.p1 = p1;
			this.p2 = p2;
		} else {
			this.p1 = p2;
			this.p2 = p1;
		}
		initializeMC(p1, p2);
	}

	/**
	 * calculates slope and intercept of a straight line through starting and ending
	 * {@link Point}
	 * 
	 * 
	 */
	public void initializeMC(Point p1, Point p2) {
		m = (p1.getY() - p2.getY()) / (p1.getX() - p2.getX());
		
		c = p1.getY() - m * p1.getX(); 
	}

	/**
	 * @return string contains starting and ending {@link Point}
	 */
	public String toString() {
		return new String(p1.toString() + " " + p2.toString()
				+ "\n");
	}

	/**
	 * intersects a {@link Line} with another with equation: m_1 * x + c_1 = m2_
	 * *x + c_2
	 * 
	 * @param someLine
	 *            {@link Line} with which this should intersect
	 * @return intersection ({@link Point}), null if there is no intersection
	 */
	public Point intersect(Line someLine) {
		Point intersection = null;
		if (Math.abs(getM()- someLine.getM()) > MYEPSILON) {
//			if (getM() != someLine.getM()) {
			
			if (p1.getX() == p2.getX()) {
				if (someLine.isOnLine(p1.getX())
						&& isinYRange(someLine, p1.getX())) {

					intersection = new Point(p1.getX(), someLine.getM() * p1.getX()
							+ someLine.getC());
				} else
					intersection =  null;
			} else if (someLine.getP1().getX() == someLine.getP2().getX()) {
				if (isOnLine(someLine.getP1().getX())
						&& someLine.isinYRange(this, someLine.getP1().getX())) {

					intersection =  new Point(someLine.getP1().getX(), getM()
							* someLine.getP1().getX() + getC());

				} else
					intersection = null;
			}

			else {

				double x = (someLine.getC() - getC())
						/ (getM() - someLine.getM());
				if (isOnLine(x) && someLine.isOnLine(x)) {
					intersection =  new Point(x, getM() * x + getC());
				}
			}
		}
		else{
			intersection = null;
		} 
		return intersection;

	}

	/**
	 * calculates whether function value y to given value of x lies in range of
	 * y_1 and y_2 of passed line
	 * 
	 * @param someLine
	 *            use equation of this straight line to calculate function value
	 *            y
	 * @return true / false
	 */
	private boolean isinYRange(Line someLine, double x) {
		double possibleY = someLine.getM() * x + someLine.getC();
		return ((possibleY <= p1.getY() & possibleY >= p2.getY()) | (possibleY <= p2.getY()
				& possibleY >= p1.getY()));
	}

	/**
	 * calculates whether value x lies in range of x_1 and x_2 of given line
	 * 
	 * @param x
	 *            value for a possible intersection
	 * @return true / false
	 */
	private boolean isOnLine(double x) {
		return (x + 1.9e-12) >= p1.getX() && (x - 1.9e-12) <= p2.getX();
	}

	/**
	 * tests whether there is an intersection, calls intersect(someLine) to
	 * check for Point != null
	 * 
	 * @param someLine
	 *            line with which this should intersect
	 * @return true / false
	 */
	public boolean isIntersect(Line someLine) {
		Point p = intersect(someLine);
		return p != null;
	}

	/**
	 * @return starting point

	 */
	public Point getP1() {
		return p1;
	}

	/**
	 * @return ending point

	 */
	public Point getP2() {
		return p2;
	}

	

}
